package com.zndroid.base.adapter;

import android.os.Build;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.recyclerview.widget.RecyclerView;

import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.entity.node.BaseNode;
import com.chad.library.adapter.base.listener.OnItemClickListener;
import com.chad.library.adapter.base.listener.OnItemLongClickListener;
import com.chad.library.adapter.base.provider.BaseItemProvider;
import com.chad.library.adapter.base.provider.BaseNodeProvider;
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
import com.zndroid.base.callback.IOnItemClickListener;
import com.zndroid.base.callback.IOnItemLongClickListener;
import com.zndroid.base.exception.SupplyIsEmptyException;
import com.zndroid.base.model.BaseCheckableNode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * base adapter for common tree list
 *
 * @author lzy
 * @date 2021/5/19
 */
@SuppressWarnings("unused")
public abstract class BaseNodeAdapter extends com.chad.library.adapter.base.BaseNodeAdapter implements
        OnItemClickListener,
        OnItemLongClickListener
{
    private boolean isSelectEnable = false;
    private IOnItemClickListener<BaseCheckableNode> itemClickListener;
    private IOnItemLongClickListener<BaseCheckableNode> itemLongClickListener;
    /**增删快*/
    private final List<BaseCheckableNode> selectedList = new CopyOnWriteArrayList<BaseCheckableNode>();

    public BaseNodeAdapter() {
        plugInListener();
    }

    private void plugInListener() {
        setOnItemClickListener(this);
        setOnItemLongClickListener(this);
    }

    @Override
    public void setList(@Nullable Collection<? extends BaseNode> list) {
        if (isNotEmpty(list)) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                super.setList(listToTree((Collection<? extends BaseCheckableNode>) list));
            } else {
                super.setList(listToTree((Collection<? extends BaseCheckableNode>) list, -1));
            }
        } else {
            throw new SupplyIsEmptyException("list data instanceof BaseCheckableNode");
        }
    }

    public void setItemClickListener(IOnItemClickListener<BaseCheckableNode> itemClickListener) {
        this.itemClickListener = itemClickListener;
    }

    public void setItemLongClickListener(IOnItemLongClickListener<BaseCheckableNode> itemLongClickListener) {
        this.itemLongClickListener = itemLongClickListener;
    }

    public void toBindItem(@NonNull BaseNodeProvider nodeProvider) {
        addNodeProvider(nodeProvider);
    }

    public void toSwitchSelectable(boolean selectEnable) {
        this.isSelectEnable = selectEnable;
        notifyDataSetChanged();
    }

    public boolean isSelectEnable() {
        return isSelectEnable;
    }

    @Override
    protected void bindClick(@NonNull BaseViewHolder viewHolder) {
        viewHolder.itemView.setOnClickListener(v -> {
            int position = viewHolder.getAdapterPosition();
            if (position == RecyclerView.NO_POSITION) {
                return;
            }

            position -= getHeaderLayoutCount();
            int itemViewType = viewHolder.getItemViewType();
            BaseItemProvider provider = getItemProvider(itemViewType);

            //dispatch listener
            dispatchOnItemClick(v, position, getData().get(position));

            //provider
            provider.onClick(viewHolder, v, getData().get(position), position);
        });

        viewHolder.itemView.setOnLongClickListener(v -> {
            int position = viewHolder.getAdapterPosition();
            if (position == RecyclerView.NO_POSITION) {
                return false;
            }

            position -= getHeaderLayoutCount();
            int itemViewType = viewHolder.getItemViewType();
            BaseItemProvider provider = getItemProvider(itemViewType);

            //dispatch listener
            dispatchOnItemLongClick(v, position, getData().get(position));

            //provider
            return provider.onLongClick(viewHolder, v, getData().get(position), position);
        });
    }

    private void dispatchOnItemClick(@NonNull View view, int position, @Nullable BaseNode t) {
        if (null != itemClickListener) {
            if (t instanceof BaseCheckableNode) {
                itemClickListener.onBackItemClicked(view, position, (BaseCheckableNode) t);
            }
        }
    }

    private void dispatchOnItemLongClick(@NonNull View view, int position, @Nullable BaseNode t) {
        if (null != itemClickListener) {
            if (t instanceof BaseCheckableNode) {
                itemLongClickListener.onBackItemLongClicked(view, position, (BaseCheckableNode) t);
            }
        }
    }

    @Override
    public void onItemClick(@NonNull BaseQuickAdapter<?, ?> adapter, @NonNull View view, int position) {
        //invalid
        //ignore
    }

    @Override
    public boolean onItemLongClick(@NonNull BaseQuickAdapter adapter, @NonNull View view, int position) {
        //invalid
        //ignore
        return false;
    }

    /**
     * item will be select or not
     *
     * @param t item entity
     * @param checked true or false
     * */
    public void toSelectItem(BaseNode t, boolean checked) {
        select(t, checked);

        notifyDataSetChanged();
    }

    /**
     * select all
     * */
    public void toSelectAll() {
        for (BaseNode it : getData()) {
            if (it instanceof  BaseCheckableNode) {
                toSelectItem(it, true);
            }
        }
    }

    /**
     * unselect all
     * */
    public void toUnSelectAll() {
        for (BaseNode it : getData()) {
            if (it instanceof  BaseCheckableNode) {
                toSelectItem(it, false);
            }
        }
    }

    /**
     * has selected
     *
     * @param expectList expectList
     * */
    private void toSelect(List<BaseNode> expectList) {
        //TODO by lazy
    }

    /**
     * get all selected item list,it contains parent and child(not duplication eliminating)
     * see also: {@link #getSelectedParent()} and {@link #getSelectedChild()}
     *
     * @return list
     * */
    public List<BaseCheckableNode> getSelectedList() {
        selectedList.clear();

        //getData为当前展开的条目数
        for (BaseNode n : getData()) {
            if (n instanceof BaseCheckableNode) {
                if (((BaseCheckableNode) n).isChecked()) {
                    selectedList.add((BaseCheckableNode) n);
                }
            }
        }
        return selectedList;
    }

    public List<BaseCheckableNode> getSelectedParent() {
        return null;
    }

    public List<BaseCheckableNode> getSelectedChild() {

        return null;
    }

    private void select(BaseNode n, boolean checked) {
        if (n instanceof BaseCheckableNode) {
            if (isNotEmpty(n.getChildNode())) {
                for (BaseNode it : n.getChildNode()) {
                    select(it, checked);
                }
            }

            ((BaseCheckableNode) n).setChecked(checked);
        }
    }

    /**
     * check 'collection' is empty or not
     *
     * @param collection your collection
     * @return true or false
     * */
    public boolean isNotEmpty(Collection<?> collection) {
        return null != collection && !collection.isEmpty();
    }

    private Collection<? extends BaseCheckableNode> listToTree(Collection<? extends BaseCheckableNode> list, long parentId) {
        List<BaseCheckableNode> tree = new ArrayList<>();
        for (BaseCheckableNode node : list) {
            if (parentId == node.getParentId()) {
                tree.add(findChild(node, list));
            }
        }

        return tree;
    }

    private BaseCheckableNode findChild(BaseCheckableNode node, Collection<? extends BaseCheckableNode> list) {
        for (BaseCheckableNode n : list) {
            if (n.getParentId() == node.getId()) {
                if (node.getChildNode() == null) {
                    node.setChildren(new ArrayList<>());
                }
                node.getChildNode().add(findChild(n, list));
            }
        }

        return node;
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    private Collection<? extends BaseCheckableNode> listToTree(Collection<? extends BaseCheckableNode> list) {
        Map<Long, List<BaseCheckableNode>> groupMap = new HashMap<>();
        list.forEach(it -> {
            List<BaseCheckableNode> children = groupMap.getOrDefault(it.getParentId(), new ArrayList<>());
            children.add(it);
            groupMap.put(it.getParentId(), children);
        });

        list.forEach(it -> {
            for (BaseNode n : Objects.requireNonNull(groupMap.getOrDefault(it.getId(), new ArrayList<>()))) {
                it.addChild(n);
            }
        });

        return list.stream()
                .filter(v -> v.getParentId() == -1).collect(Collectors.toList());
    }
}
